﻿using AJWPFAdmin.Core.Components.AJTable.ViewModels;
using AJWPFAdmin.Core.Crypto.AES;
using AJWPFAdmin.Core.Crypto.RSA;
using AJWPFAdmin.Core.Crypto.SMCrypto;
using AJWPFAdmin.Core.Enums;
using AJWPFAdmin.Core.ExtensionMethods;
using AJWPFAdmin.Core.Logger;
using AJWPFAdmin.Core.Models;
using AJWPFAdmin.Core.Models.EF.Tables;
using AJWPFAdmin.Core.Utils;
using AJWPFAdmin.Services.EF;
using AJWPFAdmin.Services.EF.TableExts;
using com.github.xiangyuecn.rsacsharp;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Wordprocessing;
using Masuit.Tools.Reflection;
using Masuit.Tools.Security;
using MaterialDesignThemes.Wpf;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;
using Prism.Ioc;
using Quartz.Logging;
using Quartz.Util;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Reflection.Emit;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;
using Yitter.IdGenerator;
using static Org.BouncyCastle.Crypto.Engines.SM2Engine;

namespace AJWPFAdmin.Services.AppointAPI
{
    public class TangShanAppointAPI : AppointAPIBase
    {
        private APIConfig _apiConfig;

        private System.Timers.Timer _deviceStatusTimer;
        private System.Timers.Timer _illegalVehicleTimer;
        private System.Timers.Timer _excessiveEmissionsTimer;

        private static readonly object _tokenLocker = new object();

        private static readonly string[] _ignoreErrorKeyword = new string[] { "不能编辑", "审核中", "未找到符合条件" };

        public override string[] IgoreErrorKeyWords { get => _ignoreErrorKeyword; set => base.IgoreErrorKeyWords = value; }

        private static readonly string[] _imageFields = new[] { "onboardListImage",
            "passingImage",
            "passingOverImage",
            "drivingLicenseImage",
            "engineNameplate",
            "carNameplate",
            "vehicleLable" };

        private const string openapiSymmetricCryKey = "openapi-symmetricCryKey";
        private const string authorization = "Authorization";
        private const string openapiUuid = "openapi-uuid";
        private const string openapiSign = "openapi-sign";
        private const string openapiApi = "openapi-api";
        private const string openapiMethod = "openapi-method";
        private const string openapiCode = "openapi-code";
        private const string openapiMessage = "openapi-message";

        public TangShanAppointAPI(ApplicationType appType,
            SystemConfigDictionary cfg,
            IContainerProvider svcProvider) : base(appType, cfg, svcProvider)
        {

        }

        private bool CheckCarImages(string carNo, string imgJsonStr)
        {
            var imgs = CommonUtil.TryGetJSONObject<string[]>(imgJsonStr ?? "[]") ?? Array.Empty<string>();

            if (imgs.Length == 0)
            {
                return false;
            }

            foreach (var img in imgs)
            {
                if (CommonRegex.HTTPURLREGEX.IsMatch(img) || !File.Exists(Path.Combine(_carIdentificationConfig.ImageSavePath, carNo, Path.GetFileName(img))))
                {
                    return false;
                }
            }
            return true;
        }

        public override Task<ProcessResult> CheckCarInfoAsync(CarInfoParams @params)
        {
            ////TODO: 本地调试模式,上线后注释
            //return Task.FromResult(ProcessResult.DefaultSuccess);

            var result = new ProcessResult();
            if (@params?.CarInfo == null)
            {
                result.SetError("参数错误");
                return Task.FromResult(result);
            }

            var carNo = @params.CarInfo.CarNo;

            if (@params.CarInfo.TypeId == (long)CarTypeEnum.厂内运输车)
            {
                var noAttachments = !CheckCarImages(carNo, @params.CarInfo.Attachments);

                var noLicense = !CheckCarImages(@params.CarInfo.CarNo, @params.CarInfo.VehicleLicense);

                if (string.IsNullOrWhiteSpace(@params.CarInfo.VIN)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.EnvRegCode)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.CarBrandModel)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.FuelTypeCode)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.Owner)
                    || (noAttachments || noLicense)
                    || @params.CarInfo.ProductionDate == null
                    || @params.CarInfo.RegDate == null
                    )
                {
                    result.SetError("信息不齐");
                }
            }
            else if (@params.CarInfo.TypeId == (long)CarTypeEnum.非道路移动机械)
            {
                var noEngineNameplate = !CheckCarImages(carNo, @params.CarInfo.EngineNameplate);

                var noNameplate = !CheckCarImages(carNo, @params.CarInfo.Nameplate);

                var noMachineEnvProtectionLabel = !CheckCarImages(carNo, @params.CarInfo.MachineEnvProtectionLabel);

                if (string.IsNullOrWhiteSpace(@params.CarInfo.PIN)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.MachineModel)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.EngineNo)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.EngineManufacturer)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.EngineModel)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.FuelTypeCode)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.Owner)
                    || (noEngineNameplate || noNameplate || noMachineEnvProtectionLabel)
                    || @params.CarInfo.ProductionDate == null
                    )
                {
                    result.SetError("信息不齐");
                }
            }
            else if (@params.CarInfo.TypeId == (long)CarTypeEnum.进出厂车
                || @params.CarInfo.TypeId == (long)CarTypeEnum.固定车)
            {
                var noAttachments = !CheckCarImages(carNo, @params.CarInfo.Attachments);

                var noLicense = !CheckCarImages(carNo, @params.CarInfo.VehicleLicense);

                if (string.IsNullOrWhiteSpace(@params.CarInfo.VIN)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.TruckTypeCode)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.FuelTypeCode)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.UsageDescCode)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.EngineNo)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.TeamName)
                    || string.IsNullOrWhiteSpace(@params.CarInfo.CarBrandModel)

                    || (noAttachments || noLicense)

                    || @params.CarInfo.RegDate == null
                    || @params.CarInfo.LatestInspectionDate == null)
                {
                    result.SetError("信息不齐");
                }
            }
            result.SetSuccess();
            return Task.FromResult(result);
        }

        protected override void OnAPILogsRaised(PassagewayDirection? direction,
            string logs)
        {
            base.OnAPILogsRaised(direction, logs);
        }

        public override async Task<ProcessResult> QueryBlackListAsync(CarInfoParams @params)
        {
            ////TODO: 本地调试模式,上线后注释
            //return new ProcessResult
            //{
            //    Success = true,
            //    Data = false
            //};

            var result = await PostAsync("query", "hmdck", new JObject {
                {"cph", @params.CarInfo.CarNo},
                {"cpys", ((short)@params.CarInfo.PlateColor).ToString()},
            });

            if (!result.Success)
            {
                return result;
            }

            var obj = result.Data as JObject;
            if (obj == null)
            {
                //result.SetError("数据错误");
                //return result;
                result.SetSuccess(false);
                return result;
            }

            result.SetSuccess(obj["flag"].ToObject<bool>());

            return result;
        }

        public override async Task<ProcessResult> QueryExcessiveEmissionsListAsync(QueryIllegalVehicleParams @params)
        {
            ////TODO: 本地调试模式,上线后注释
            //return new ProcessResult
            //{
            //    Success = true,
            //    Data = new AJTableFetchResult
            //    {
            //        HasNext = false,
            //        Total = 0,
            //        Rows = new List<ExcessiveEmissionsListItem>()
            //    }
            //};

            var result = await PostAsync("issue", "queryIllegalVehicleList", new JObject {
                {"companyNo", _apiConfig.CompanyCode},
                //{"plateNo", string.IsNullOrWhiteSpace(@params.CarNo) ? string.Empty : @params.CarNo},
                //{"plateColor", @params.Color == null ?string.Empty : ((short)@params.Color).ToString()},
                //{"startTime", @params.StartDate == null ? string.Empty : @params.StartDate.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss")},
                //{"endTime", @params.EndDate == null ? string.Empty : @params.EndDate.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss")},
                {"start", 0 },
                {"end", 10000 },
            }, isListResponse: true);

            if (!result.Success)
            {
                return result;
            }

            var array = result.Data as JArray;

            var rows = new List<ExcessiveEmissionsListItem>();

            if (array == null)
            {
                result.SetSuccess(new AJTableFetchResult
                {
                    HasNext = false,
                    Total = 0,
                    Rows = rows
                });

                return result;
            }

            var fuelTypeOptions = await SystemDictionaryExt.GetSystemDictionariesAsync(db, SystemDictionaryExt.SystemDictionaryKey.燃料);

            foreach (var token in array)
            {
                var colorStr = token["plateColor"]?.ToString() ?? ((int)CarPlateColor.其他).ToString();

                _ = Enum.TryParse<CarPlateColor>(colorStr, out var color);

                var emissionStandardStr = token["emissionStandard"]?.ToString() ?? ((int)EmissionStandard.未知).ToString();

                _ = Enum.TryParse<EmissionStandard>(emissionStandardStr, out var emissionStandard);

                var deliveryReasonStr = token["deliveryReason"]?.ToString() ?? ((int)ExcessiveEmissionsReason.其他).ToString();

                _ = Enum.TryParse<ExcessiveEmissionsReason>(deliveryReasonStr, out var deliveryReason);

                var fuelTypeStr = token["fuelType"]?.ToString();

                rows.Add(new ExcessiveEmissionsListItem
                {
                    CarNo = token["plateNo"]?.ToString(),
                    Color = color,
                    VIN = token["vin"]?.ToString(),
                    FuelType = fuelTypeOptions.FirstOrDefault(p => p.Code == fuelTypeStr)?.Name ?? fuelTypeStr,
                    EmissionStandard = emissionStandard,
                    DeliveryReason = deliveryReason,
                    State = (token["state"]?.ToString() ?? "0").TryGetInt() == 1,
                    DeliveryTime = token["deliveryTime"]?.ToString(),
                    ReleaseTime = token["releaseTime"]?.ToString(),
                }); ;
            }

            var paged = new AJTableFetchResult
            {
                HasNext = false,
                Total = array.Count,
                Rows = rows
            };

            result.SetSuccess(paged);

            return result;
        }

        public override Task<ProcessResult> CheckIllegalVehiclesAsync(CheckIllegalVehicleParams @params)
        {
            var now = DateTime.Now;
            return PostAsync("inspect", "checkIllegalVehicles", new JObject {
                {"companyNo", _apiConfig.CompanyCode},
                {"plateNo", @params.PlateNo},
                {"plateColor", ((short)@params.PlateColor).ToString()},
                {"vin", @params.VIN },
                {"checkResult", @params.CheckResult },
                {"checkDate", now.ToString("yyyy-MM-dd") },
                {"fleetName", @params.FleetName },
                {"uploadDateTime", now.ToString("yyyyMMddHHmmss")}
            }, true);

        }

        public override async Task<ProcessResult> QueryIllegalVehicleListAsync(QueryIllegalVehicleParams @params)
        {
            ////TODO: 本地调试模式,上线后注释
            //return new ProcessResult
            //{
            //    Success = true,
            //    Data = new AJTableFetchResult
            //    {
            //        HasNext = false,
            //        Total = 0,
            //        Rows = new List<IllegalVehicleListItem>()
            //    }
            //};

            var result = await PostAsync("issue", "queryIllegalVehicleRecord", new JObject {
                {"companyNo", _apiConfig.CompanyCode},
                //{"plateNo", string.IsNullOrWhiteSpace(@params.CarNo) ? string.Empty : @params.CarNo},
                //{"plateColor", @params.Color == null ? string.Empty :  ((short)@params.Color).ToString()},
                {"startTime", @params.StartDate == null ? string.Empty : @params.StartDate.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss")},
                {"endTime", @params.EndDate == null ? string.Empty : @params.EndDate.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss")},
                {"start", 0 },
                {"end", 500 },
            }, isListResponse: true);

            if (!result.Success)
            {
                return result;
            }

            var array = result.Data as JArray;

            var rows = new List<IllegalVehicleListItem>();

            if (array == null)
            {
                result.SetSuccess(new AJTableFetchResult
                {
                    HasNext = false,
                    Total = 0,
                    Rows = rows
                });

                return result;
            }

            foreach (var token in array)
            {
                var colorStr = token["plateColor"]?.ToString() ?? ((int)CarPlateColor.其他).ToString();

                _ = Enum.TryParse<CarPlateColor>(colorStr, out var color);

                rows.Add(new IllegalVehicleListItem
                {
                    CarNo = token["plateNo"]?.ToString(),
                    Color = color,
                    VIN = token["vin"]?.ToString(),
                    EntryTime = token["entryTime"]?.ToString(),
                    DeliveryTime = token["deliveryTime"]?.ToString(),
                    Reason = token["reason"]?.ToString(),
                    Remark = token["remark"]?.ToString(),
                });
            }

            var paged = new AJTableFetchResult
            {
                HasNext = false,
                Total = array.Count,
                Rows = rows
            };

            result.SetSuccess(paged);

            return result;
        }

        public override async Task<ProcessResult> QueryManageMeasureListAsync(QueryManageMeasureParams @params)
        {
            ////TODO: 本地调试模式,上线后注释
            //return new ProcessResult
            //{
            //    Success = true,
            //    Data = new AJTableFetchResult
            //    {
            //        HasNext = false,
            //        Total = 0,
            //        Rows = new List<ManageMeasureListItem>()
            //    }
            //};

            var result = await PostAsync("issue", "queryMeasure", new JObject {
                {"companyNo", _apiConfig.CompanyCode}
            }, true);

            if (!result.Success)
            {
                return result;
            }

            var array = result.Data as JArray;

            var rows = new List<ManageMeasureListItem>();

            foreach (var token in array)
            {
                var levelStr = token["warnLevel"]?.ToString() ?? ((int)ManageMeasureWarnLevel.自定义管控).ToString();

                _ = Enum.TryParse<ManageMeasureWarnLevel>(levelStr, out var level);

                var resLevelStr = token["responseLevel"]?.ToString() ?? ((int)ManageMeasureResponseLevel.Ⅰ级).ToString();

                _ = Enum.TryParse<ManageMeasureResponseLevel>(resLevelStr, out var resLevel);

                rows.Add(new ManageMeasureListItem
                {
                    WarnStartTime = token["warnStartTime"]?.ToString(),
                    WarnEndTime = token["warnEndTime"]?.ToString(),
                    WarnLevel = level,
                    ResponseLevel = resLevel,
                    ControlMeasure = token["controlMeasure"]?.ToString()
                });
            }

            var paged = new AJTableFetchResult
            {
                HasNext = false,
                Total = array.Count,
                Rows = rows
            };

            result.SetSuccess(paged);

            return result;
        }

        public override Task<ProcessResult> UploadDataVerificationAsync(DataVerificationParams @params)
        {
            return PostAsync("inspect", "dataVerification", new JObject
            {
                {"companyNo",_apiConfig.CompanyCode },
                {"vechicleNum",@params.VechicleNum },
                {"inVechicleNum",@params.InVechicleNum },
                {"nrVehicleNum",@params.NrVehicleNum },
                {"vechiclePassingNum",@params.VechiclePassingNum },
                {"inVechiclePassingNum",@params.InVechiclePassingNum },
                {"nrVechiclePassingNum",@params.NrVechiclePassingNum },
                {"cargoLedgerNum",@params.CargoLedgerNum },
                {"checkVechicleNum",@params.CheckVechicleNum },
                {"recordDate",@params.RecordDate.ToString("yyyy-MM-dd") },
                {"uploadDateTime",DateTime.Now.ToString("yyyyMMddHHmmss") },
            });
        }

        public override Task<ProcessResult> UploadCarInfoAsync(CarInfoParams @params)
        {
            ////TODO: 本地调试模式,上线后注释
            //return Task.FromResult(ProcessResult.DefaultSuccess);

            var (api, body) = ProcessCarInfoToUpload(@params);
            if (string.IsNullOrEmpty(api))
            {
                return Task.FromResult(ProcessResult.DefaultSuccess);
            }

            return PostAsync(api, @params.Operation == DataOperationType.新增 ? "sync" : "modify", body,
                needUILog: true,
                direction: @params.Passageway?.Direction);
        }

        public override Task<ProcessResult> UploadPssagewayAsync(PssagewayParams @params)
        {
            return PostAsync("enterprises", @params.Operation == DataOperationType.新增 ? "/trun/sync" : "/trun/modify", new JObject
            {
                {"companyNo", _apiConfig.CompanyCode },
                {"entryNo", @params.Watchhouse.Code },
                {"trunNo", @params.Passageway.Code },
                {"trunName", @params.Passageway.Name },
                {"address", @params.Passageway.Address },
                {"latitude", @params.Passageway.Longitude },
                {"longitude", @params.Passageway.Latitude },
                {"managerPerson", @params.Passageway.ManagerPerson },
                {"managerPhone", @params.Passageway.ManagerPhone },
                {"managerCompany", @params.Passageway.ManagerCompany },
                {"companyPerson", @params.Passageway.CompanyPerson },
                {"companyPhone", @params.Passageway.CompanyPhone },
                {"uploadDateTime", DateTime.Now.ToString("yyyyMMddHHmmss") },
            });
        }

        public override Task<ProcessResult> UploadDeviceInfoAsync(DeviceInfoParams @params)
        {
            ////TODO: 本地调试模式,上线后注释
            //return Task.FromResult(ProcessResult.DefaultSuccess);

            if (@params.Device.Type >= DeviceType.高频读头)
            {
                return Task.FromResult(ProcessResult.DefaultSuccess);
            }
            if (@params.Operation == DataOperationType.移除)
            {
                return PostAsync("camera", "delete", new JObject
                {
                    {"companyNo", _apiConfig.CompanyCode },
                    {"cameraNo", @params.Device.Code },
                });
            }
            return PostAsync("camera", @params.Operation == DataOperationType.新增 ? "sync" : "modify", new JObject
            {
                {"companyNo", _apiConfig.CompanyCode },
                {"entryNo", @params.Watchhouse.Code },
                {"trunNo", @params.Passageway.Code },
                {"cameraNo", @params.Device.Code },
                {"cameraName", @params.Device.Name },
                {"ipAddress", @params.Device.IP },
                {"username", @params.Device.LoginName },
                {"password", @params.Device.LoginPassword },
                {"videosrc", @params.Device.VideoPlayRTSPUrl },
                {"videosrchf", @params.Device.VideoPlayBackRTSPUrl },
                {"entryType", ((int)@params.Passageway.Direction).ToString()  },
                {"uploadDateTime", DateTime.Now.ToString("yyyyMMddHHmmss") },
            });
        }

        public override async Task<ProcessResult> UploadShippingRecordAsync(ShippingRecordParams @params)
        {
            ////TODO: 本地调试模式,上线后注释
            //return new ProcessResult
            //{
            //    Success = true,
            //    Attach = new UploadShippingRecordResponse
            //    {
            //        GoodsNum = 1,
            //        RecordNum = 1
            //    }
            //};

            ProcessResult result;
            var response = new UploadShippingRecordResponse();

            if (@params.Record.TypeId == (long)CarTypeEnum.进出厂车
                || @params.Record.TypeId == (long)CarTypeEnum.固定车)
            {
                // 强制为新增
                @params.Operation = DataOperationType.新增;
            }

            var processRet = ProcessShippingRecordToUpload(@params, @params.Record.Direction);
            if (string.IsNullOrEmpty(processRet.api))
            {
                result = new ProcessResult();
                result.SetSuccess();
                result.Attach = response;
                return result;
            }

            result = await PostAsync(processRet.api, processRet.method, processRet.body);
            if (result.Success)
            {
                // 过车记录上传成功,记录号加1
                response.RecordNum++;
            }

            result.Attach = response;

            // 如果是进厂 则不先上传进厂的物资信息,等出厂的时候补传
            if (@params.Record.Direction == PassagewayDirection.进)
            {
                return result;
            }

            @params.Operation = DataOperationType.更新;

            #region 上传入厂物资

            var processRetOfJinChuGoods = ProcessShippingRecordToUpload(@params, PassagewayDirection.进);

            result = await PostAsync(processRetOfJinChuGoods.api,
                processRetOfJinChuGoods.method,
                processRetOfJinChuGoods.body,
                needUILog: true,
                direction: @params.Record.Direction,
                relRecord: @params.Record);
            if (!result.Success)
            {
                result.SetError($"{@params.Record.CardNo} {KeyDescription}接口 [入厂] 物资信息上传失败:{result.Message}");
            }
            else
            {
                // 货物记录上传成功,物资记录号加1
                response.GoodsNum++;
            }

            #endregion

            #region 上传出厂物资

            processRetOfJinChuGoods = ProcessShippingRecordToUpload(@params, PassagewayDirection.出);

            result = await PostAsync(processRetOfJinChuGoods.api,
                processRetOfJinChuGoods.method,
                processRetOfJinChuGoods.body,
                needUILog: true,
                direction: @params.Record.Direction);
            if (!result.Success)
            {
                result.SetError($"{@params.Record.CardNo} {KeyDescription}接口 [出厂] 物资信息上传失败:{result.Message}");
            }
            else
            {
                // 货物记录上传成功,物资记录号加1
                response.GoodsNum++;
            }

            #endregion

            result.Attach = response;

            return result;

        }

        //public override async Task<ProcessResult> GetCarInfoAsync(CarInfoParams @params)
        //{
        //    var result = await PostAsync("query", "vehicle", new JObject
        //    {
        //        {"plateNo", @params.CarInfo.CarNo },
        //        {"companyNo", _apiConfig.CompanyCode }
        //    });

        //    if (!result.Success)
        //    {
        //        return result;
        //    }

        //    var obj = result.Data as JObject;

        //    var plateColor = obj["plateColor"]?.ToString() ?? CarPlateColor.蓝牌.ToString();
        //    if (Enum.TryParse(plateColor, true, out CarPlateColor color))
        //    {
        //        @params.CarInfo.PlateColor = color;
        //    }

        //    var truckTypeStr = obj["carType"]?.ToString();
        //    if (!string.IsNullOrWhiteSpace(truckTypeStr))
        //    {
        //        var rootId = (long)SystemDictionaryExt.SystemDictionaryKey.货车;
        //        var truckType = db.SystemDictionaries.AsNoTracking()
        //            .FirstOrDefault(p => p.ParentId == rootId && (p.Code == truckTypeStr));
        //        if (truckType != null && @params.CarInfo.TruckTypeCode != truckType.Code)
        //        {
        //            @params.CarInfo.TruckTypeCode = truckType.Code;
        //            @params.CarInfo.TruckTypeName = truckType.Name;
        //        }
        //    }

        //    var fuelTypeStr = obj["fuelType"]?.ToString();
        //    if (!string.IsNullOrWhiteSpace(fuelTypeStr))
        //    {
        //        var rootId = (long)SystemDictionaryExt.SystemDictionaryKey.燃料;
        //        var fuelType = db.SystemDictionaries.AsNoTracking()
        //            .FirstOrDefault(p => p.ParentId == rootId && p.Code == fuelTypeStr);
        //        if (fuelType != null && @params.CarInfo.FuelTypeCode != fuelType.Code)
        //        {
        //            @params.CarInfo.FuelTypeCode = fuelType.Code;
        //            @params.CarInfo.FuelTypeName = fuelType.Name;
        //        }
        //    }

        //    var vin = obj["vin"]?.ToString();
        //    if (@params.CarInfo.VIN != vin)
        //    {
        //        @params.CarInfo.VIN = vin;
        //    }

        //    var registerDate = obj["registerDate"]?.ToString();
        //    if (!string.IsNullOrWhiteSpace(registerDate))
        //    {
        //        @params.CarInfo.RegDate = $"{registerDate[..4]}-{registerDate.Substring(4, 2)}-{registerDate.Substring(6, 2)}".TryGetDate();
        //    }

        //    var brandModel = obj["brandModel"]?.ToString();
        //    if (!string.IsNullOrWhiteSpace(brandModel))
        //    {
        //        @params.CarInfo.CarBrandModel = brandModel;
        //    }

        //    var emissionStandard = obj["emissionStandard"]?.ToString() ?? EmissionStandard.未知.GetDescription();
        //    var emissionStandardEnums = Enum.GetValues(typeof(EmissionStandard));
        //    foreach (EmissionStandard enumValue in emissionStandardEnums)
        //    {
        //        if (enumValue.GetDescription().Equals(emissionStandard))
        //        {
        //            @params.CarInfo.EmissionStandard = enumValue;
        //            break;
        //        }
        //    }

        //    var onlineStatus = obj["onlineStatus"]?.ToObject<int>();
        //    if (onlineStatus.HasValue)
        //    {
        //        @params.CarInfo.NetworkingStatus = onlineStatus == 1;
        //    }

        //    var useNature = obj["useNature"]?.ToString();
        //    if (!string.IsNullOrWhiteSpace(useNature))
        //    {
        //        var parentId = (long)SystemDictionaryExt.SystemDictionaryKey.使用性质;
        //        var useDescDic = db.SystemDictionaries.AsNoTracking()
        //            .FirstOrDefault(p => p.ParentId == parentId && p.Code == useNature);
        //        if (useDescDic != null && @params.CarInfo.UsageDescCode != useDescDic.Code)
        //        {
        //            @params.CarInfo.UsageDescCode = useDescDic.Code;
        //            @params.CarInfo.UsageDescription = useDescDic.Name;
        //        }
        //    }

        //    var fleetnName = obj["fleetnName"]?.ToString();
        //    if (!string.IsNullOrWhiteSpace(fleetnName))
        //    {
        //        @params.CarInfo.TeamName = fleetnName;
        //    }

        //    result.SetSuccess(@params.CarInfo);

        //    return result;

        //}

        private Task<ProcessResult> LoginAsync()
        {
            return PostAsync("user", "login", new JObject
                {
                    {"username", _apiConfig.Account },
                    {"password", _apiConfig.Password },
                });
        }

        private (string api, string method, JObject body) ProcessShippingRecordToUpload(ShippingRecordParams @params, PassagewayDirection direction = PassagewayDirection.进)
        {
            var api = string.Empty;
            JObject body = null;
            var method = "sync";

            // 超过固定车范围的不做操作
            if (@params.Record.TypeId > (long)CarTypeEnum.固定车)
            {
                return (api, method, body);
            }

            var rcdNum = direction == PassagewayDirection.进
                ? @params.Record.RecordNum - 1
                : @params.Record.RecordNum + 1;

            if (rcdNum <= 0)
            {
                rcdNum = 1;
            }

            var carNo = @params.Record.CarNo;

            var directionNum = direction == PassagewayDirection.进 ? "1" : "2";
            var rsn = $"{_apiConfig.CompanyCode}{@params.Record.CreateDate.ToString("yyyyMMddHHmm")}{directionNum}{rcdNum.ToString().PadLeft(3, '0')}";

            if (@params.Record.TypeId == (long)CarTypeEnum.厂内运输车)
            {
                api = "inVehicles";
                method = "passingRecord/sync";
                body = new JObject
                {
                    {nameof(rsn),rsn },
                    {"companyNo",_apiConfig.CompanyCode },

                    {"entryNo",direction == PassagewayDirection.进
                                ?  @params.Record.WatchhouseCode : @params.Record.OutWatchhouseCode },

                    {"trunNo",direction == PassagewayDirection.进
                                ? @params.Record.PassagewayCode :  @params.Record.OutPassagewayCode},

                    {"cameraNo",direction == PassagewayDirection.进
                                ? (@params.Record.DeviceCode ?? "") :  (@params.Record.OutDeviceCode ?? string.Empty)},

                    {"vin",@params.Record.VIN },
                    {"registerCode",@params.Record.EnvRegCode },
                    {"passingTime",direction == PassagewayDirection.进
                                ? @params.Record.ShipStartDate.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss")
                                : @params.Record.ShipEndDate.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss") },

                    {"passingType",directionNum },
                    {"uploadDateTime", DateTime.Now.ToString("yyyyMMddHHmmss") },
                };
            }
            else if (@params.Record.TypeId == (long)CarTypeEnum.非道路移动机械)
            {
                api = "nonRoadVehicles";
                method = "passingRecord/sync";
                body = new JObject
                {
                    {nameof(rsn),rsn },
                    {"companyNo",_apiConfig.CompanyCode },
                    {"entryNo",direction == PassagewayDirection.进
                                ?  @params.Record.WatchhouseCode : @params.Record.OutWatchhouseCode },

                    {"trunNo",direction == PassagewayDirection.进
                                ? @params.Record.PassagewayCode :  @params.Record.OutPassagewayCode},

                    {"cameraNo",direction == PassagewayDirection.进
                                ? (@params.Record.DeviceCode ?? "") :  (@params.Record.OutDeviceCode ?? string.Empty)},
                    {"pin",@params.Record.PIN },
                    {"registerCode",@params.Record.EnvRegCode },
                    {"passingTime",direction == PassagewayDirection.进
                                ? @params.Record.ShipStartDate.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss")
                                : @params.Record.ShipEndDate.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss") },
                    {"passingType",directionNum },
                    {"uploadDateTime", DateTime.Now.ToString("yyyyMMddHHmmss") },
                };
            }
            else if (@params.Record.TypeId == (long)CarTypeEnum.进出厂车)
            {
                api = @params.Operation == DataOperationType.更新 ? "cargoLedger" : "passingRecord";

                if (@params.Operation == DataOperationType.更新)
                {
                    var freightVolume = direction == PassagewayDirection.进 ? @params.Record.InGoodsVolume : @params.Record.OutGoodsVolume;

                    var goodsName = direction == PassagewayDirection.进 ? @params.Record.InGoodsName : @params.Record.OutGoodsName;

                    if (string.IsNullOrWhiteSpace(goodsName))
                    {
                        goodsName = "空车";
                    }

                    body = new JObject
                    {
                        {nameof(rsn), rsn },
                        {"shipName",  goodsName},
                        {"freightVolume", Math.Round(freightVolume,2,MidpointRounding.AwayFromZero)},
                        {"freightUnit", @params.Record.FreightUnit.GetDescription() },
                        {"fleetName", @params.Record.TeamName },
                        {"uploadDateTime", DateTime.Now.ToString("yyyyMMddHHmmss") },
                    };
                }
                else
                {
                    var (carHead, _, carTail) = @params.Record.GetWatchPositionImageFile(@params.Record.Direction);

                    body = new JObject
                    {
                        {nameof(rsn),rsn },
                        {"companyNo",_apiConfig.CompanyCode },
                        {"entryNo",direction == PassagewayDirection.进
                                ?  @params.Record.WatchhouseCode : @params.Record.OutWatchhouseCode },

                        {"trunNo",direction == PassagewayDirection.进

                                ? @params.Record.PassagewayCode :  @params.Record.OutPassagewayCode},

                        {"cameraNo",direction == PassagewayDirection.进
                                ? (@params.Record.DeviceCode ?? "") :  (@params.Record.OutDeviceCode ?? string.Empty)},

                        {"passingTime",direction == PassagewayDirection.进
                                ? @params.Record.ShipStartDate.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss")
                                : @params.Record.ShipEndDate.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss") },

                        {"passingType",directionNum },
                        {"passingImage",  CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, carHead), needHeader: false,true,500)},
                        {"passingOverImage",  CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, carTail), needHeader: false,true,500)},
                        {"pendulumState",  @params.Record.AutoPass ? "2" : "1"},
                        {"plateNo",  @params.Record.CarNo},
                        {"plateColor",  ((byte)@params.Record.PlateColor).ToString()},
                        {"uploadDateTime", DateTime.Now.ToString("yyyyMMddHHmmss") },
                    };
                }
            }
            return (api, method, body);
        }

        private (string api, JObject body) ProcessCarInfoToUpload(CarInfoParams @params)
        {
            var api = string.Empty;
            JObject body = null;
            var carNo = @params.CarInfo.CarNo;
            if (@params.CarInfo.TypeId == (long)CarTypeEnum.厂内运输车)
            {
                api = "inVehicle";
                var onboardListImage = string.Empty;
                var drivingLicenseImage = string.Empty;
                body = new JObject
                {
                    {"companyNo", _apiConfig.CompanyCode },
                    {"vin", @params.CarInfo.VIN},
                    {"registerCode", @params.CarInfo.EnvRegCode},
                    {"managerCode", ""},
                    {"productionDate", @params.CarInfo.ProductionDate.GetValueOrDefault().ToString("yyyyMMdd")},
                    {"registerDate", @params.CarInfo.RegDate.GetValueOrDefault().ToString("yyyyMMdd") },
                    {"brandModel", @params.CarInfo.CarBrandModel },
                    {"emissionStandard", @params.CarInfo.EmissionStandard.GetDescription() },
                    {"engineNum", @params.CarInfo.EngineNo},
                    {"fuelType", @params.CarInfo.FuelTypeCode },
                    {"vehicleOwner", @params.CarInfo.Owner },
                    {"onlineStatus", @params.CarInfo.NetworkingStatus ? "1" : "0" },
                    {nameof(onboardListImage), onboardListImage },
                    {nameof(drivingLicenseImage), drivingLicenseImage },
                    {"uploadDateTime", DateTime.Now.ToString("yyyyMMddHHmmss") },
                };

                onboardListImage = @params.CarInfo.GetFileOfPropertyName(nameof(Car.Attachments));
                if (!string.IsNullOrEmpty(onboardListImage))
                {
                    body[nameof(onboardListImage)] = CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, onboardListImage),
                        needHeader: false);
                }

                drivingLicenseImage = @params.CarInfo.GetFileOfPropertyName(nameof(Car.VehicleLicense));
                if (!string.IsNullOrEmpty(drivingLicenseImage))
                {
                    body[nameof(drivingLicenseImage)] = CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, drivingLicenseImage), needHeader: false);
                }
            }
            else if (@params.CarInfo.TypeId == (long)CarTypeEnum.非道路移动机械)
            {
                api = "nonRoadVehicle";
                var engineNameplate = string.Empty;
                var carNameplate = string.Empty;
                var vehicleLable = string.Empty;

                body = new JObject
                {
                    {"companyNo", _apiConfig.CompanyCode },
                    {"registerCode", @params.CarInfo.EnvRegCode},
                    {"cphm", @params.CarInfo.CarNo},
                    {"pin", @params.CarInfo.PIN},
                    {"vehicleModel", @params.CarInfo.MachineModel},
                    {"productionDate", @params.CarInfo.MechanicalProdDate.GetValueOrDefault().ToString("yyyyMMdd")},
                    {"vehicleType", (int)@params.CarInfo.MachineType.GetValueOrDefault()},
                    {"engineNum", @params.CarInfo.EngineNo},
                    {"engineVendor", @params.CarInfo.EngineManufacturer},
                    {"engineModel", @params.CarInfo.EngineModel},

                    {nameof(engineNameplate), engineNameplate},
                    {nameof(carNameplate), carNameplate},
                    {nameof(vehicleLable), vehicleLable},

                    {"emissionStandard", @params.CarInfo.EmissionStandard.GetDescription() },
                    {"fuelType", @params.CarInfo.FuelTypeCode },
                    {"vehicleOwner", @params.CarInfo.Owner },
                    {"uploadDateTime", DateTime.Now.ToString("yyyyMMddHHmmss") },
                };

                engineNameplate = @params.CarInfo.GetFileOfPropertyName(nameof(Car.EngineNameplate));
                if (!string.IsNullOrEmpty(engineNameplate))
                {
                    body[nameof(engineNameplate)] = CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, engineNameplate), needHeader: false);
                }

                carNameplate = @params.CarInfo.GetFileOfPropertyName(nameof(Car.Nameplate));
                if (!string.IsNullOrEmpty(carNameplate))
                {
                    body[nameof(carNameplate)] = CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, carNameplate), needHeader: false);
                }

                vehicleLable = @params.CarInfo.GetFileOfPropertyName(nameof(Car.MachineEnvProtectionLabel));
                if (!string.IsNullOrEmpty(vehicleLable))
                {
                    body[nameof(vehicleLable)] = CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, vehicleLable), needHeader: false);
                }
            }
            else if (@params.CarInfo.TypeId == (long)CarTypeEnum.进出厂车)
            {
                api = "vehicle";
                var onboardListImage = string.Empty;
                var drivingLicenseImage = string.Empty;
                body = new JObject
                {
                    {"companyNo", _apiConfig.CompanyCode },
                    {"vin", @params.CarInfo.VIN},
                    {"plateNo", @params.CarInfo.CarNo },
                    {"plateColor", ((byte)@params.CarInfo.PlateColor).ToString() },
                    {"carType", @params.CarInfo.TruckTypeCode },
                    {"registerDate", @params.CarInfo.RegDate.GetValueOrDefault().ToString("yyyyMMdd") },
                    {"brandModel", @params.CarInfo.CarBrandModel },
                    {"fuelType", @params.CarInfo.FuelTypeCode },
                    {"emissionStandard", @params.CarInfo.EmissionStandard.GetDescription() },
                    {"onlineStatus", @params.CarInfo.NetworkingStatus ? "1" : "0" },
                    {"useNature", @params.CarInfo.UsageDescCode },
                    {"engineNo", @params.CarInfo.EngineNo },

                    {nameof(onboardListImage), onboardListImage },
                    {nameof(drivingLicenseImage), drivingLicenseImage },

                    {"fleetName", @params.CarInfo.TeamName },
                    {"vehicleType", ((int)@params.CarInfo.VehicleType).ToString() },
                    {"latestInspectionDate", @params.CarInfo.LatestInspectionDate.GetValueOrDefault().ToString("yyyyMMdd") },
                    {"uploadDateTime", DateTime.Now.ToString("yyyyMMddHHmmss") },
                };

                onboardListImage = @params.CarInfo.GetFileOfPropertyName(nameof(Car.Attachments));
                if (!string.IsNullOrEmpty(onboardListImage))
                {
                    body[nameof(onboardListImage)] = CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, onboardListImage), needHeader: false);
                }

                drivingLicenseImage = @params.CarInfo.GetFileOfPropertyName(nameof(Car.VehicleLicense));
                if (!string.IsNullOrEmpty(drivingLicenseImage))
                {
                    body[nameof(drivingLicenseImage)] = CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, drivingLicenseImage), needHeader: false);
                }
            }

            return (api, body);
        }

        private async Task<ProcessResult> PostAsync(string api,
            string method,
            JObject body,
            bool isListResponse = false, bool needUILog = false,
            PassagewayDirection? direction = null,
            ShippingRecord relRecord = null)
        {
            var result = new ProcessResult();

            var stringBuilder = new StringBuilder();

            using var httpClient = CreateClient();

            try
            {
                if (method != "login" &&
                    (string.IsNullOrEmpty(_apiConfig.Authorization) || DateTime.Now > _apiConfig.TokenExpireDate))
                {
                    result = await LoginAsync();
                    if (!result.Success)
                    {
                        return result;
                    }

                    _apiConfig.Authorization = result.Data.ToString();
                    _apiConfig.TokenExpireDate = DateTime.Now.AddMinutes(20);

                    try
                    {
                        lock (_tokenLocker)
                        {
                            var apiKey = Config.Key;
                            // 重新查出来保存登录token
                            using var db = DbService.GetDbContext(_configService);

                            Config = db.SystemConfigDictionaries.FirstOrDefault(p => p.Key == apiKey);

                            Config.StringValue = CommonUtil.AJSerializeObject(_apiConfig);
                            db.Entry(Config).State = EntityState.Modified;

                            db.SaveChanges();
                        }
                    }
                    catch (Exception)
                    {
                    }

                }

                var bodyStr = body.ToString(Newtonsoft.Json.Formatting.None);

                var uuid = Guid.NewGuid().ToString("N");
                var uuidBytes = Encoding.UTF8.GetBytes(uuid);

                byte[] encryptedBodyBytes = null;
                string sign = string.Empty;

                if (method != "login")
                {
                    var _aesKey = Convert.ToBase64String(Encoding.UTF8.GetBytes(Core.Properties.Resources.AESKey[..16]));

                    encryptedBodyBytes = AJAES.EncryptWithBase64KeyECB(Encoding.UTF8.GetBytes(bodyStr), _aesKey);

                    //if (httpClient.DefaultRequestHeaders.Contains(openapiSymmetricCryKey))
                    //{
                    //    httpClient.DefaultRequestHeaders.Remove(openapiSymmetricCryKey);
                    //}

                    //if (httpClient.DefaultRequestHeaders.Contains(authorization))
                    //{
                    //    httpClient.DefaultRequestHeaders.Remove(authorization);
                    //}

                    httpClient.DefaultRequestHeaders.TryAddWithoutValidation(openapiSymmetricCryKey, AJRSA.Encrypt(_aesKey, _apiConfig.RSAPublicKey));

                    httpClient.DefaultRequestHeaders.TryAddWithoutValidation(authorization, $"Bearer {_apiConfig.Authorization}");

                    sign = Convert.ToBase64String(AJRSA.Sign(encryptedBodyBytes.Concat(uuidBytes).ToArray(), _apiConfig.RSAPrivateKey, AJRSA.KeyFormat.PKCS8));

                    stringBuilder.Append($"uuid:{uuid}\r\n");
                }
                else
                {
                    using var alg = SHA256.Create();
                    encryptedBodyBytes = SM2.EncryptToBytes(bodyStr, _apiConfig.SM2PublicKey);
                    sign = Convert.ToBase64String(alg.ComputeHash(encryptedBodyBytes.Concat(uuidBytes).ToArray()));

                }

                //if (httpClient.DefaultRequestHeaders.Contains(openapiUuid))
                //{
                //    httpClient.DefaultRequestHeaders.Remove(openapiUuid);
                //}
                //if (httpClient.DefaultRequestHeaders.Contains(openapiSign))
                //{
                //    httpClient.DefaultRequestHeaders.Remove(openapiSign);
                //}
                //if (httpClient.DefaultRequestHeaders.Contains(openapiApi))
                //{
                //    httpClient.DefaultRequestHeaders.Remove(openapiApi);
                //}
                //if (httpClient.DefaultRequestHeaders.Contains(openapiMethod))
                //{
                //    httpClient.DefaultRequestHeaders.Remove(openapiMethod);
                //}

                httpClient.DefaultRequestHeaders.TryAddWithoutValidation(openapiUuid, uuid);
                httpClient.DefaultRequestHeaders.TryAddWithoutValidation(openapiSign, sign);

                httpClient.DefaultRequestHeaders.TryAddWithoutValidation(openapiApi, api);
                httpClient.DefaultRequestHeaders.TryAddWithoutValidation(openapiMethod, method);

                if (method != "login")
                {
                    stringBuilder.Append($"{KeyDescription} {method}/{api}:\r\n");

                    var props = body.Properties();
                    foreach (var prop in props)
                    {
                        if (_imageFields.Contains(prop.Name))
                        {
                            if (!string.IsNullOrWhiteSpace(prop.Value?.ToString()))
                            {
                                body[prop.Name] = "有图片";
                            }
                        }
                    }
                }

                if (relRecord != null)
                {
                    stringBuilder.Append($"当前关联车牌号:{relRecord.CarNo}\r\n");
                }

                stringBuilder.Append($"请求参数:\r\n{body}\r\n");
                var headers = string.Join("\r\n", httpClient.DefaultRequestHeaders.Select(p => $"{p.Key}:{p.Value.FirstOrDefault()}"));
                stringBuilder.Append($"请求header:\r\n{headers}\r\n");

                using var ms = new MemoryStream(encryptedBodyBytes);
                var content = new StreamContent(ms);

                var res = await httpClient.PostAsync(_apiConfig.Url, content);

                var responseStr = string.Empty;

                var responseBytes = await res.Content.ReadAsByteArrayAsync();
                if ((responseBytes?.Length).GetValueOrDefault() == 0)
                {
                    var hdCode = res.Headers.GetValues(openapiCode).FirstOrDefault();
                    var hdMsg = Encoding.UTF8.GetString(Convert.FromBase64String(res.Headers.GetValues(openapiMessage).FirstOrDefault() ?? string.Empty));
                    result.SetError($"服务接口未响应任何数据:{hdCode}-{hdMsg}");

                    responseStr = await res.Content.ReadAsStringAsync();

                    stringBuilder.Append($"接口响应源内容\r\n:{responseStr}\r\ncode:{hdCode}\r\nmsg:{hdMsg}");
                    var logContentInner = stringBuilder.ToString();
                    _logger.Error(logContentInner);

                    if (needUILog)
                    {
                        OnAPILogsRaised(direction, logContentInner);
                    }

                    return result;
                }

                if (method == "login")
                {
                    responseStr = SM2.Decrypt(responseBytes, _apiConfig.SM2PrivateKey);
                }
                else
                {
                    if (res.Headers.TryGetValues(openapiSymmetricCryKey, out var key))
                    {
                        var base64Str = Convert.ToBase64String(responseBytes);

                        var aesKeyBase64 = AJRSA.Decrypt(key.FirstOrDefault() ?? string.Empty, _apiConfig.RSAPrivateKey, AJRSA.KeyFormat.PKCS8);

                        responseStr = AJAES.DecryptWithBase64KeyECB(base64Str, aesKeyBase64);
                    }
                    else
                    {
                        responseStr = await res.Content.ReadAsStringAsync();
                    }

                    stringBuilder.Append($"接口响应源内容\r\n:{responseStr}");
                }

                var response = CommonUtil.TryGetJSONObject<APIResponse>(responseStr) ?? new APIResponse
                {
                    code = -1,
                    message = "解析接口返回失败",
                };

                var logContent = stringBuilder.ToString();

                if (response.code != 10000)
                {
                    // 如果接口返回提示重复, 说明数据已经同步过了, 最好返回success,告知后续处理,至于后面会不会变, 再看情况定制更改
                    var responseMsgStr = response.message ?? string.Empty;
                    if (responseMsgStr.Contains("重复")
                        || responseMsgStr.Contains("已存在"))
                    {
                        result.SetSuccess();
                        // 记录下日志
                        _logger.Info(logContent);
                        return result;
                    }
                    result.SetError(response.message ?? "解析失败");

                    _logger.Error(logContent);
                    if (needUILog)
                    {
                        OnAPILogsRaised(direction, logContent);
                    }
                }
                else
                {
                    if (method == "login")
                    {
                        result.SetSuccess(response.data?.ToString());
                    }
                    else
                    {
                        result.SetSuccess(isListResponse
                            ? (response.data == null ? null : JArray.FromObject(response.data))
                            : (response.data == null ? null
                                : response.data is string ? response.data : JObject.FromObject(response.data)));
                    }

                    if (needUILog)
                    {
                        OnAPILogsRaised(direction, logContent);
                    }

                    _logger.Error(logContent);
                }

            }
            catch (Exception e)
            {
                stringBuilder.AppendLine(e.Message);
                result.SetError(stringBuilder.ToString(), e);
                if (stringBuilder != null)
                {
                    stringBuilder.Append(e.StackTrace);
                    _logger.Error(stringBuilder.ToString());
                }
                if (needUILog)
                {
                    OnAPILogsRaised(direction, stringBuilder.ToString());
                }

                if (e is TaskCanceledException && method == "hmdck")
                {
                    // 如果是黑名单超时,则判断为不是黑名单 --阿吉 2025年7月27日11点46分
                    result.SetSuccess(false);
                    return result;
                }
            }
            finally
            {
                stringBuilder?.Clear();
            }
            return result;
        }

        private HttpClient CreateClient()
        {
            var httpClient = CommonUtil.CreateCommonHttpClient(10);

            httpClient.DefaultRequestHeaders.TryAddWithoutValidation("openapi-callerId", _apiConfig.CallerId);
            httpClient.DefaultRequestHeaders.TryAddWithoutValidation("openapi-dataType", "TEXT");
            httpClient.DefaultRequestHeaders.TryAddWithoutValidation("client_id", _apiConfig.ClientId);
            httpClient.DefaultRequestHeaders.TryAddWithoutValidation("client_secret", _apiConfig.ClientSecret);
            httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/octet-stream");

            return httpClient;
        }

        public override Task ConvertToCustomConfig(bool isReal)
        {
            _apiConfig = CommonUtil.TryGetJSONObject<APIConfig>(Config.StringValue) ?? new APIConfig();
            _customAPIConfig = _apiConfig;
            AutoUploadDataVerification = _apiConfig.AutoUploadDataVerification;

            if (isReal)
            {
                if (_appType == ApplicationType.岗亭端)
                {
                    // 开启一个定时器, 每隔开30秒上传视频设备状态
                    _deviceStatusTimer = new System.Timers.Timer
                    {
                        AutoReset = true,
                        Interval = TimeSpan.FromSeconds(30).TotalMilliseconds,
                    };
                    _deviceStatusTimer.Elapsed += DeviceHeartbeatTimerHandler;
                    _deviceStatusTimer.Start();

                    // 违规车辆定时器
                    _illegalVehicleTimer = new System.Timers.Timer
                    {
                        AutoReset = true,
                        Interval = TimeSpan.FromHours(1.5).TotalMilliseconds,
                    };
                    _illegalVehicleTimer.Elapsed += IllegalVehicleTimerHandler;
                    _illegalVehicleTimer.Start();

                    // 超标排放定时器
                    _excessiveEmissionsTimer = new System.Timers.Timer
                    {
                        AutoReset = true,
                        Interval = TimeSpan.FromHours(1).TotalMilliseconds,
                    };
                    _excessiveEmissionsTimer.Elapsed += ExcessiveEmissionsTimerHandler;
                    _excessiveEmissionsTimer.Start();
                }
            }
            else
            {
                _ = GetSchemas();
            }

            return Task.CompletedTask;
        }

        private async void IllegalVehicleTimerHandler(object sender, System.Timers.ElapsedEventArgs e)
        {
            // 这里必须重新创建个dbcontent,因为在定时器线程中, 和全局的dbcontext隔离开
            var db = DbService.GetDbContext(_configService);
            var end = DateTime.Today;
            var start = end.AddDays(-90);
            var result = await QueryIllegalVehicleListAsync(new QueryIllegalVehicleParams
            {
                StartDate = start,
                EndDate = end,
            });

            if (!result.Success)
            {
                _logger.Error($"{KeyDescription} 违规车辆接口查询失败:{result.Message}");
                return;
            }

            var logBuilder = new StringBuilder();

            var paged = result.Data as AJTableFetchResult;

            if (paged == null || paged.Rows == null || paged.Rows.Count() == 0)
            {
                logBuilder.AppendLine("接口没有返回任何数据");
                _logger.Info($"{KeyDescription} 违规车辆接口同步数据:{logBuilder}");
                return;
            }

            var source = paged.Rows.Select(p => (IllegalVehicleListItem)p).ToList();

            var carNos = source.Select(p => p.CarNo).Distinct().ToArray();
            var apiKey = Config.Key;
            var dbList = db.IllegalVehicleRecords
                .Where(p => carNos.Contains(p.CarNo) && p.API == apiKey).ToList();

            var dbCarNos = dbList.Select(p => p.CarNo).ToArray();

            var updated = false;

            var removed = dbCarNos.Except(carNos).ToArray();
            if (removed.Length > 0)
            {
                db.IllegalVehicleRecords.RemoveRange(dbList.Where(p => removed.Contains(p.CarNo)).ToList());
                logBuilder.AppendLine($"已移除数据:{removed.Length}");
                updated = true;
            }

            var date = DateTime.Now;

            var added = carNos.Except(dbCarNos).ToArray();

            if (added.Length > 0)
            {
                db.IllegalVehicleRecords.AddRange(source.Where(p => added.Contains(p.CarNo))
                    .Select(p => new IllegalVehicleRecord
                    {
                        Id = YitIdHelper.NextId(),
                        CarNo = p.CarNo,
                        EntryTime = p.EntryTime.TryGetDate(),
                        DeliveryTime = p.DeliveryTime.TryGetDate(),
                        API = apiKey,
                        Color = p.Color,
                        Reason = p.Reason,
                        Remark = p.Remark,
                        VIN = p.VIN,
                        CreateDate = date,
                    }).ToList());
                logBuilder.AppendLine($"已新增数据:{added.Length}");
                updated = true;
            }

            var same = carNos.Intersect(dbCarNos).ToArray();
            if (same.Length > 0)
            {
                foreach (var carNo in same)
                {
                    var dbData = dbList.FirstOrDefault(p => p.CarNo == carNo);
                    var sourceData = source.FirstOrDefault(p => p.CarNo == carNo);

                    dbData.Reason = sourceData.Reason;
                    dbData.Remark = sourceData.Remark;
                    dbData.EntryTime = sourceData.EntryTime.TryGetDate();
                    dbData.DeliveryTime = sourceData.DeliveryTime.TryGetDate();
                    dbData.Color = sourceData.Color;
                    dbData.VIN = sourceData.VIN;
                    dbData.UpdateDate = date;
                    db.Entry(dbData).State = EntityState.Modified;
                }
                logBuilder.AppendLine($"已更新数据:{same.Length}");
                updated = true;
            }

            if (updated)
            {
                await db.SaveChangesAsync();
                _logger.Info($"{KeyDescription} 违规车辆接口同步数据:{logBuilder}");
            }
        }

        private async void ExcessiveEmissionsTimerHandler(object sender, System.Timers.ElapsedEventArgs e)
        {
            // 这里必须重新创建个dbcontent,因为在定时器线程中, 和全局的dbcontext隔离开
            var db = DbService.GetDbContext(_configService);
            //var end = DateTime.Today;
            //var start = end.AddDays(-90);
            var result = await QueryExcessiveEmissionsListAsync(new QueryIllegalVehicleParams
            {
                //StartDate = start,
                //EndDate = end,
            });

            if (!result.Success)
            {
                _logger.Error($"{KeyDescription} 超标排放接口查询失败:{result.Message}");
                return;
            }

            var logBuilder = new StringBuilder();

            var paged = result.Data as AJTableFetchResult;

            if (paged == null || paged.Rows == null || paged.Rows.Count() == 0)
            {
                logBuilder.AppendLine("接口没有返回任何数据");
                _logger.Info($"{KeyDescription} 超标排放接口同步数据:{logBuilder}");
                return;
            }

            var source = paged.Rows.Select(p => (ExcessiveEmissionsListItem)p).ToList();

            var carNos = source.Select(p => p.CarNo).Distinct().ToArray();
            var apiKey = Config.Key;
            var dbList = db.EmiRiseRecords.Where(p => carNos.Contains(p.CarNo) && p.API == apiKey).ToList();

            var dbCarNos = dbList.Select(p => p.CarNo).ToArray();

            var updated = false;

            var removed = dbCarNos.Except(carNos).ToArray();
            if (removed.Length > 0)
            {
                db.EmiRiseRecords.RemoveRange(dbList.Where(p => removed.Contains(p.CarNo)).ToList());
                logBuilder.AppendLine($"已移除数据:{removed.Length}");
                updated = true;
            }

            var date = DateTime.Now;

            var added = carNos.Except(dbCarNos).ToArray();

            if (added.Length > 0)
            {
                db.EmiRiseRecords.AddRange(source.Where(p => added.Contains(p.CarNo))
                    .Select(p => new EmiRiseRecord
                    {
                        Id = YitIdHelper.NextId(),
                        CarNo = p.CarNo,
                        ExceedInfo = p.DeliveryReason.ToString(),
                        ExceedFlag = p.State ? 1 : 0,
                        StartTime = p.DeliveryTime.TryGetDate(),
                        EndTime = p.State ? p.ReleaseTime.TryGetDate() : null,
                        Synchronized = true,
                        API = apiKey,
                        Color = p.Color,
                        EmissionStandard = p.EmissionStandard,
                        VIN = p.VIN,
                        FuelType = p.FuelType,
                        CreateDate = date,
                    }).ToList());
                logBuilder.AppendLine($"已新增数据:{added.Length}");
                updated = true;
            }

            var same = carNos.Intersect(dbCarNos).ToArray();
            if (same.Length > 0)
            {
                foreach (var carNo in same)
                {
                    var dbData = dbList.FirstOrDefault(p => p.CarNo == carNo);
                    var sourceData = source.FirstOrDefault(p => p.CarNo == carNo);

                    dbData.ExceedInfo = sourceData.DeliveryReason.ToString();
                    dbData.ExceedFlag = sourceData.State ? 1 : 0;
                    dbData.StartTime = sourceData.DeliveryTime.TryGetDate();
                    dbData.EndTime = sourceData.State ? sourceData.ReleaseTime.TryGetDate() : null;
                    dbData.Color = sourceData.Color;
                    dbData.EmissionStandard = sourceData.EmissionStandard;
                    dbData.VIN = sourceData.VIN;
                    dbData.FuelType = sourceData.FuelType;
                    dbData.UpdateDate = date;
                    db.Entry(dbData).State = EntityState.Modified;
                }
                logBuilder.AppendLine($"已更新数据:{same.Length}");
                updated = true;
            }

            if (updated)
            {
                await db.SaveChangesAsync();
                _logger.Info($"{KeyDescription} 超标排放接口同步数据:{logBuilder}");
            }
        }

        private async void DeviceHeartbeatTimerHandler(object sender, System.Timers.ElapsedEventArgs e)
        {
            // 这里必须重新创建个dbcontent,因为在定时器线程中, 和全局的dbcontext隔离开
            var db = DbService.GetDbContext(_configService);

            var codes = db.Devices.Where(p => p.Synchronized && p.OnlyMonitor).AsNoTracking().Select(p => p.Code).ToList();

            db.Dispose();

            var tasks = new List<Task<ProcessResult>>();

            foreach (var code in codes)
            {
                var result = await PostAsync("camera", "heartbeat", new JObject
                {
                    {"companyNo",_apiConfig.CompanyCode },
                    {"cameraNo",code },
                });
                if (!result.Success)
                {
                    _logger.Error($"{KeyDescription} 视频设备 {code} 心跳传输失败:{result.Message}");
                }
            }
        }

        public new void Dispose()
        {
            _deviceStatusTimer?.Stop();
            _excessiveEmissionsTimer?.Stop();
            _illegalVehicleTimer?.Stop();
        }

        public class APIConfig : APIConfigBase
        {
            [DynamicFormItemDefine("接口地址")]
            public string Url { get; set; }

            [DynamicFormItemDefine("登录认证Token", Disable = true)]
            public string Authorization { get; set; }
            [DynamicFormItemDefine("登录认证Token过期时间", Disable = true)]
            public DateTime TokenExpireDate { get; set; }

            [DynamicFormItemDefine("SM2公钥", VbenAdminFormType.InputTextArea)]
            public string SM2PublicKey { get; set; }
            [DynamicFormItemDefine("SM2私钥", VbenAdminFormType.InputTextArea)]
            public string SM2PrivateKey { get; set; }
            [DynamicFormItemDefine("RSA公钥", VbenAdminFormType.InputTextArea)]
            public string RSAPublicKey { get; set; }
            [DynamicFormItemDefine("RSA私钥", VbenAdminFormType.InputTextArea)]
            public string RSAPrivateKey { get; set; }
            [DynamicFormItemDefine("账户名")]
            public string Account { get; set; }
            [DynamicFormItemDefine("登录密码")]
            public string Password { get; set; }
            [DynamicFormItemDefine("鉴权码")]
            public string CallerId { get; set; }
            [DynamicFormItemDefine("客户Id")]
            public string ClientId { get; set; }
            [DynamicFormItemDefine("客户密钥")]
            public string ClientSecret { get; set; }
            [DynamicFormItemDefine("公司编号")]
            public string CompanyCode { get; set; }
            [DynamicFormItemDefine("公司名称")]
            public string CompanyName { get; set; }
        }

        public class APIResponse
        {
            public int code { get; set; }
            public string message { get; set; }
            public object data { get; set; }
        }
    }
}
